﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.IO;
using Demo.WorldMap.GPU;
using Demo.WorldMap.Prefab;
using Dou.Clip;
using Dou.Utils;
using UnityEngine;

namespace Demo.WorldMap
{
    /// <summary>
    /// 场景静态数据渲染器
    /// </summary>
    [DefaultExecutionOrder(100)]
    public class SceneStaticDataRenderer : MonoBehaviour, ISceneStaticRenderer
    {
        /// <summary>
        /// 关联相机的唯一ID
        /// 注：为空时表示使用默认相机
        /// </summary>
        public string cameraID;
        
        /// <summary>
        /// 导出数据对象
        /// </summary>
        public StaticRenderData data;
        
        /// <summary>
        /// LOD 设置对象
        /// </summary>
        public StaticRenderLOD lodSetting;

        private SceneStaticCameraCtl _cameraCtl;

        private Stream _dataStream;

        private SceneStaticGPURender[] _gpuRenders;
        private SceneStaticPrefabRender[] _prefabRenders;
        
        private Dictionary<string, ISceneStaticRender> _renderMap = new Dictionary<string, ISceneStaticRender>();

        private float _lastCameraHeight;
        
        void OnEnable()
        {
            _cameraCtl = SceneStaticRendererMgr.instance.GetCameraCtl(cameraID);
            
            _dataStream = StreamUtil.GetDataStream(WorldMapAssetUtil.GetAssetPath(data.dataFileName));
            
            var gpuCount = data.gpu.trees.Length;
            _gpuRenders = new SceneStaticGPURender[gpuCount];
            for (int i = 0; i < gpuCount; i++)
            {
                var tree = data.gpu.trees[i];
                var gpuRender = new SceneStaticGPURender();
                gpuRender.cameraCtl = _cameraCtl;
                gpuRender.tree = tree;
                gpuRender.dataStream = _dataStream;
                gpuRender.OnEnable();
                _gpuRenders[i] = gpuRender;
                _renderMap.Add(tree.name, gpuRender);
            }

            var prefabCount = data.prefab.trees.Length;
            _prefabRenders = new SceneStaticPrefabRender[prefabCount];
            for (int i = 0; i < prefabCount; i++)
            {
                var tree = data.prefab.trees[i];
                var prefabRender = new SceneStaticPrefabRender();
                prefabRender.cameraCtl = _cameraCtl;
                prefabRender.parent = transform;
                prefabRender.res = data.prefab.res;
                prefabRender.tree = tree;
                prefabRender.OnEnable();
                _prefabRenders[i] = prefabRender;
                _renderMap.Add(tree.name, prefabRender);
            }
            
            SceneStaticRendererMgr.instance.AddRender(this);

            UpdateLodSetting();
        }

        private void UpdateLodSetting()
        {
            var cameraHeight = _cameraCtl.camera.transform.position.y;
            if (Mathf.Approximately(_lastCameraHeight, cameraHeight))
            {
                return;
            }
            _lastCameraHeight = cameraHeight;

            for (int i = 0; i < lodSetting.groups.Length; i++)
            {
                var group = lodSetting.groups[i];
                if (_renderMap.TryGetValue(group.name, out var render))
                {
                    var setting = group.lods[^1];
                    for (int j = 0; j < group.lods.Length; j++)
                    {
                        var lod = group.lods[i];
                        if (cameraHeight <= lod.cameraHeight)
                        {
                            setting = lod;
                            break;
                        }
                    }
                    render.SetEnable(setting.show);
                    render.SetLOD(setting.lod);
                }
            }
        }

        void Update()
        {
            UpdateLodSetting();
            
            for (int i = 0; i < _gpuRenders.Length; i++)
            {
                var gpuRender = _gpuRenders[i];
                gpuRender.Update();
            }

            for (int i = 0; i < _prefabRenders.Length; i++)
            {
                var prefabRender = _prefabRenders[i];
                prefabRender.Update();
            }
        }

        void LateUpdate()
        {
            for (int i = 0; i < _gpuRenders.Length; i++)
            {
                var gpuRender = _gpuRenders[i];
                gpuRender.LateUpdate();
            }

            for (int i = 0; i < _prefabRenders.Length; i++)
            {
                var prefabRender = _prefabRenders[i];
                prefabRender.LateUpdate();
            }
        }

        void OnDisable()
        {
            for (int i = 0; i < _gpuRenders.Length; i++)
            {
                var gpuRender = _gpuRenders[i];
                gpuRender.OnDisable();
            }
            _gpuRenders = null;

            for (int i = 0; i < _prefabRenders.Length; i++)
            {
                var prefabRender = _prefabRenders[i];
                prefabRender.OnDisable();
            }
            _prefabRenders = null;
            
            SceneStaticRendererMgr.instance.RemoveRender(this);
        }
        
#if UNITY_EDITOR
        
        // 绘制指定名称的 BVH 树线框
        public string drawTreeBoundsName = String.Empty;

        // 绘制所有最终绘制的对象的AABB
        public bool drawObjAABB = false;
        
        private void OnDrawGizmos()
        {
            if (data == null)
            {
                return;
            }
            
            if (!string.IsNullOrWhiteSpace(drawTreeBoundsName))
            {
                foreach (var tree in data.gpu.trees)
                {
                    if (tree.name == drawTreeBoundsName)
                    {
                        foreach (var node in tree.nodes)
                        {
                            if (node.clippingType == FrustumClippingType.Cross)
                            {
                                Gizmos.color = node.isLeaf ? Color.red : Color.magenta;
                            }
                            else if (node.clippingType == FrustumClippingType.Inner)
                            {
                                Gizmos.color = node.isLeaf ? Color.yellow : Color.green;
                            }
                            else
                            {
                                Gizmos.color = node.isLeaf ? Color.white : Color.gray;
                            }
                            Gizmos.DrawWireCube(node.bounds.center, node.bounds.size);
                        }
                        break;
                    }
                }
                foreach (var tree in data.prefab.trees)
                {
                    if (tree.name == drawTreeBoundsName)
                    {
                        foreach (var node in tree.nodes)
                        {
                            if (node.clippingType == FrustumClippingType.Cross)
                            {
                                Gizmos.color = node.isLeaf ? Color.red : Color.magenta;
                            }
                            else if (node.clippingType == FrustumClippingType.Inner)
                            {
                                Gizmos.color = node.isLeaf ? Color.yellow : Color.green;
                            }
                            else
                            {
                                Gizmos.color = node.isLeaf ? Color.white : Color.gray;
                            }
                            Gizmos.DrawWireCube(node.bounds.center, node.bounds.size);
                        }
                        break;
                    }
                }
            }

            if (drawObjAABB)
            {
                foreach (var render in _gpuRenders)
                {
                    render.DrawObjAABB();
                }
                foreach (var render in _prefabRenders)
                {
                    render.DrawObjAABB();
                }
            }
        }

#endif
        
    }
}
